home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2009 February / PCWFEB09.iso / Software / Resources / Browsers, Managers & Extensions / Mozilla Weave 0.2.7 / latest-weave.xpi / modules / service.js < prev    next >
Text File  |  2008-09-19  |  35KB  |  985 lines

  1. /* ***** BEGIN LICENSE BLOCK *****
  2.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3.  *
  4.  * The contents of this file are subject to the Mozilla Public License Version
  5.  * 1.1 (the "License"); you may not use this file except in compliance with
  6.  * the License. You may obtain a copy of the License at
  7.  * http://www.mozilla.org/MPL/
  8.  *
  9.  * Software distributed under the License is distributed on an "AS IS" basis,
  10.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  11.  * for the specific language governing rights and limitations under the
  12.  * License.
  13.  *
  14.  * The Original Code is Bookmarks Sync.
  15.  *
  16.  * The Initial Developer of the Original Code is Mozilla.
  17.  * Portions created by the Initial Developer are Copyright (C) 2007
  18.  * the Initial Developer. All Rights Reserved.
  19.  *
  20.  * Contributor(s):
  21.  *  Dan Mills <thunder@mozilla.com>
  22.  *  Myk Melez <myk@mozilla.org>
  23.  *
  24.  * Alternatively, the contents of this file may be used under the terms of
  25.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  26.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  27.  * in which case the provisions of the GPL or the LGPL are applicable instead
  28.  * of those above. If you wish to allow use of your version of this file only
  29.  * under the terms of either the GPL or the LGPL, and not to allow others to
  30.  * use your version of this file under the terms of the MPL, indicate your
  31.  * decision by deleting the provisions above and replace them with the notice
  32.  * and other provisions required by the GPL or the LGPL. If you do not delete
  33.  * the provisions above, a recipient may use your version of this file under
  34.  * the terms of any one of the MPL, the GPL or the LGPL.
  35.  *
  36.  * ***** END LICENSE BLOCK ***** */
  37.  
  38. const EXPORTED_SYMBOLS = ['Weave'];
  39.  
  40. const Cc = Components.classes;
  41. const Ci = Components.interfaces;
  42. const Cr = Components.results;
  43. const Cu = Components.utils;
  44.  
  45. // The following constants determine when Weave will automatically sync data.
  46.  
  47. // An interval of one minute, initial threshold of 100, and step of 5 means
  48. // that we'll try to sync each engine 21 times, once per minute, at
  49. // consecutively lower thresholds (from 100 down to 5 in steps of 5 and then
  50. // one more time with the threshold set to the minimum 1) before resetting
  51. // the engine's threshold to the initial value and repeating the cycle
  52. // until at some point the engine's score exceeds the threshold, at which point
  53. // we'll sync it, reset its threshold to the initial value, rinse, and repeat.
  54.  
  55. // How long we wait between sync checks.
  56. const SCHEDULED_SYNC_INTERVAL = 60 * 1000 * 5; // five minutes
  57.  
  58. // INITIAL_THRESHOLD represents the value an engine's score has to exceed
  59. // in order for us to sync it the first time we start up (and the first time
  60. // we do a sync check after having synced the engine or reset the threshold).
  61. const INITIAL_THRESHOLD = 75;
  62.  
  63. // THRESHOLD_DECREMENT_STEP is the amount by which we decrement an engine's
  64. // threshold each time we do a sync check and don't sync that engine.
  65. const THRESHOLD_DECREMENT_STEP = 25;
  66.  
  67. Cu.import("resource://gre/modules/XPCOMUtils.jsm");
  68. Cu.import("resource://weave/log4moz.js");
  69. Cu.import("resource://weave/constants.js");
  70. Cu.import("resource://weave/util.js");
  71. Cu.import("resource://weave/wrap.js");
  72. Cu.import("resource://weave/faultTolerance.js");
  73. Cu.import("resource://weave/crypto.js");
  74. Cu.import("resource://weave/engines.js");
  75. Cu.import("resource://weave/oauth.js");
  76. Cu.import("resource://weave/dav.js");
  77. Cu.import("resource://weave/identity.js");
  78. Cu.import("resource://weave/async.js");
  79. Cu.import("resource://weave/clientData.js");
  80. Cu.import("resource://weave/engines/cookies.js");
  81. Cu.import("resource://weave/engines/bookmarks.js");
  82. Cu.import("resource://weave/engines/history.js");
  83. Cu.import("resource://weave/engines/passwords.js");
  84. Cu.import("resource://weave/engines/forms.js");
  85. Cu.import("resource://weave/engines/tabs.js");
  86. Cu.import("resource://weave/engines/input.js");
  87.  
  88. Function.prototype.async = Async.sugar;
  89.  
  90. // for export
  91. let Weave = {};
  92. Cu.import("resource://weave/constants.js", Weave);
  93. Cu.import("resource://weave/util.js", Weave);
  94. Cu.import("resource://weave/async.js", Weave);
  95. Cu.import("resource://weave/faultTolerance.js", Weave);
  96. Cu.import("resource://weave/crypto.js", Weave);
  97. Cu.import("resource://weave/notifications.js", Weave);
  98. Cu.import("resource://weave/identity.js", Weave);
  99. Cu.import("resource://weave/clientData.js", Weave);
  100. Cu.import("resource://weave/dav.js", Weave);
  101. Cu.import("resource://weave/stores.js", Weave);
  102. Cu.import("resource://weave/syncCores.js", Weave);
  103. Cu.import("resource://weave/engines.js", Weave);
  104. Cu.import("resource://weave/oauth.js", Weave);
  105. Cu.import("resource://weave/service.js", Weave);
  106. Cu.import("resource://weave/engines/cookies.js", Weave);
  107. Cu.import("resource://weave/engines/passwords.js", Weave);
  108. Cu.import("resource://weave/engines/bookmarks.js", Weave);
  109. Cu.import("resource://weave/engines/history.js", Weave);
  110. Cu.import("resource://weave/engines/forms.js", Weave);
  111. Cu.import("resource://weave/engines/tabs.js", Weave);
  112. Cu.import("resource://weave/engines/input.js", Weave);
  113.  
  114. Utils.lazy(Weave, 'Service', WeaveSvc);
  115.  
  116. /*
  117.  * Service singleton
  118.  * Main entry point into Weave's sync framework
  119.  */
  120.  
  121. function WeaveSvc() {
  122.   this._startupFinished = false;
  123.   this._initLogs();
  124.   this._log.info("Weave Sync Service Initializing");
  125.  
  126.   // Create Weave identities (for logging in, and for encryption)
  127.   ID.set('WeaveID', new Identity('Mozilla Services Password', this.username));
  128.   ID.set('WeaveCryptoID',
  129.          new Identity('Mozilla Services Encryption Passphrase', this.username));
  130.  
  131.   // Set up aliases for other modules to use our IDs
  132.   ID.setAlias('WeaveID', 'DAV:default');
  133.   ID.setAlias('WeaveCryptoID', 'Engine:PBE:default');
  134.  
  135.   // Other misc startup
  136.   Utils.prefs.addObserver("", this, false);
  137.   this._os.addObserver(this, "quit-application", true);
  138.   FaultTolerance.Service; // initialize FT service
  139.  
  140.   if (!this.enabled)
  141.     this._log.info("Weave Sync disabled");
  142. }
  143. WeaveSvc.prototype = {
  144.  
  145.   _notify: Wrap.notify,
  146.   _lock: Wrap.lock,
  147.   _localLock: Wrap.localLock,
  148.   _catchAll: Wrap.catchAll,
  149.   _osPrefix: "weave:service:",
  150.   _cancelRequested: false,
  151.   _isQuitting: false,
  152.   _loggedIn: false,
  153.   _syncInProgress: false,
  154.  
  155.   __os: null,
  156.   get _os() {
  157.     if (!this.__os)
  158.       this.__os = Cc["@mozilla.org/observer-service;1"]
  159.         .getService(Ci.nsIObserverService);
  160.     return this.__os;
  161.   },
  162.  
  163.   __dirSvc: null,
  164.   get _dirSvc() {
  165.     if (!this.__dirSvc)
  166.       this.__dirSvc = Cc["@mozilla.org/file/directory_service;1"].
  167.         getService(Ci.nsIProperties);
  168.     return this.__dirSvc;
  169.   },
  170.  
  171.   __json: null,
  172.   get _json() {
  173.     if (!this.__json)
  174.       this.__json = Cc["@mozilla.org/dom/json;1"].
  175.         createInstance(Ci.nsIJSON);
  176.     return this.__json;
  177.   },
  178.  
  179.   // object for caching public and private keys
  180.   _keyPair: {},
  181.  
  182.   // Timer object for automagically syncing
  183.   _scheduleTimer: null,
  184.  
  185.   get username() {
  186.     return Utils.prefs.getCharPref("username");
  187.   },
  188.   set username(value) {
  189.     if (value)
  190.       Utils.prefs.setCharPref("username", value);
  191.     else
  192.       Utils.prefs.clearUserPref("username");
  193.  
  194.     // fixme - need to loop over all Identity objects - needs some rethinking...
  195.     ID.get('WeaveID').username = value;
  196.     ID.get('WeaveCryptoID').username = value;
  197.   },
  198.  
  199.   get password() { return ID.get('WeaveID').password; },
  200.   set password(value) { ID.get('WeaveID').password = value; },
  201.  
  202.   get passphrase() { return ID.get('WeaveCryptoID').password; },
  203.   set passphrase(value) { ID.get('WeaveCryptoID').password = value; },
  204.  
  205.   get userPath() { return ID.get('WeaveID').username; },
  206.  
  207.   get isLoggedIn() this._loggedIn,
  208.   get isInitialized() this._initialized,
  209.  
  210.   get isQuitting() this._isQuitting,
  211.   set isQuitting(value) { this._isQuitting = value; },
  212.  
  213.   get cancelRequested() this._cancelRequested,
  214.   set cancelRequested(value) { this._cancelRequested = value; },
  215.  
  216.   get enabled() Utils.prefs.getBoolPref("enabled"),
  217.  
  218.   get schedule() {
  219.     if (!this.enabled)
  220.       return 0; // manual/off
  221.     return Utils.prefs.getIntPref("schedule");
  222.   },
  223.  
  224.   onWindowOpened: function Weave__onWindowOpened() {
  225.     if (!this._startupFinished) {
  226.       this._startupFinished = true;
  227.       if (Utils.prefs.getBoolPref("autoconnect") &&
  228.           this.username && this.username != 'nobody')
  229.         this._initialLoginAndSync.async(this);
  230.     }
  231.   },
  232.  
  233.   _initialLoginAndSync: function Weave__initialLoginAndSync() {
  234.     let self = yield;
  235.     yield this.loginAndInit(self.cb); // will throw if login fails
  236.     yield this.sync(self.cb);
  237.   },
  238.  
  239.   _setSchedule: function Weave__setSchedule(schedule) {
  240.     switch (this.schedule) {
  241.     case 0:
  242.       this._disableSchedule();
  243.       break;
  244.     case 1:
  245.       this._enableSchedule();
  246.       break;
  247.     default:
  248.       this._log.warn("Invalid Weave scheduler setting: " + schedule);
  249.       break;
  250.     }
  251.   },
  252.  
  253.   _enableSchedule: function WeaveSvc__enableSchedule() {
  254.     if (this._scheduleTimer) {
  255.       this._scheduleTimer.cancel();
  256.       this._scheduleTimer = null;
  257.     }
  258.     this._scheduleTimer = Cc["@mozilla.org/timer;1"].
  259.       createInstance(Ci.nsITimer);
  260.     let listener = new Utils.EventListener(Utils.bind2(this, this._onSchedule));
  261.     this._scheduleTimer.initWithCallback(listener, SCHEDULED_SYNC_INTERVAL,
  262.                                          this._scheduleTimer.TYPE_REPEATING_SLACK);
  263.     this._log.config("Weave scheduler enabled");
  264.   },
  265.  
  266.   _disableSchedule: function WeaveSvc__disableSchedule() {
  267.     if (this._scheduleTimer) {
  268.       this._scheduleTimer.cancel();
  269.       this._scheduleTimer = null;
  270.     }
  271.     this._log.config("Weave scheduler disabled");
  272.   },
  273.  
  274.   _onSchedule: function WeaveSvc__onSchedule() {
  275.     if (this.enabled) {
  276.       if (DAV.locked) {
  277.         this._log.info("Skipping scheduled sync; local operation in progress")
  278.       } else {
  279.         this._log.info("Running scheduled sync");
  280.         this._notify("sync", "",
  281.                      this._catchAll(this._lock(this._syncAsNeeded))).async(this);
  282.       }
  283.     }
  284.   },
  285.  
  286.   _initLogs: function WeaveSvc__initLogs() {
  287.     this._log = Log4Moz.Service.getLogger("Service.Main");
  288.     this._log.level =
  289.       Log4Moz.Level[Utils.prefs.getCharPref("log.logger.service.main")];
  290.  
  291.     let formatter = new Log4Moz.BasicFormatter();
  292.     let root = Log4Moz.Service.rootLogger;
  293.     root.level = Log4Moz.Level[Utils.prefs.getCharPref("log.rootLogger")];
  294.  
  295.     let capp = new Log4Moz.ConsoleAppender(formatter);
  296.     capp.level = Log4Moz.Level[Utils.prefs.getCharPref("log.appender.console")];
  297.     root.addAppender(capp);
  298.  
  299.     let dapp = new Log4Moz.DumpAppender(formatter);
  300.     dapp.level = Log4Moz.Level[Utils.prefs.getCharPref("log.appender.dump")];
  301.     root.addAppender(dapp);
  302.  
  303.     let brief = this._dirSvc.get("ProfD", Ci.nsIFile);
  304.     brief.QueryInterface(Ci.nsILocalFile);
  305.     brief.append("weave");
  306.     brief.append("logs");
  307.     brief.append("brief-log.txt");
  308.     if (!brief.exists())
  309.       brief.create(brief.NORMAL_FILE_TYPE, PERMS_FILE);
  310.  
  311.     let verbose = brief.parent.clone();
  312.     verbose.append("verbose-log.txt");
  313.     if (!verbose.exists())
  314.       verbose.create(verbose.NORMAL_FILE_TYPE, PERMS_FILE);
  315.  
  316.     this._briefApp = new Log4Moz.RotatingFileAppender(brief, formatter);
  317.     this._briefApp.level = Log4Moz.Level[Utils.prefs.getCharPref("log.appender.briefLog")];
  318.     root.addAppender(this._briefApp);
  319.     this._debugApp = new Log4Moz.RotatingFileAppender(verbose, formatter);
  320.     this._debugApp.level = Log4Moz.Level[Utils.prefs.getCharPref("log.appender.debugLog")];
  321.     root.addAppender(this._debugApp);
  322.   },
  323.  
  324.   clearLogs: function WeaveSvc_clearLogs() {
  325.     this._briefApp.clear();
  326.     this._debugApp.clear();
  327.   },
  328.  
  329.   _uploadVersion: function WeaveSvc__uploadVersion() {
  330.     let self = yield;
  331.  
  332.     DAV.MKCOL("meta", self.cb);
  333.     let ret = yield;
  334.     if (!ret)
  335.       throw "Could not create meta information directory";
  336.  
  337.     DAV.PUT("meta/version", STORAGE_FORMAT_VERSION, self.cb);
  338.     ret = yield;
  339.     Utils.ensureStatus(ret.status, "Could not upload server version file");
  340.   },
  341.  
  342.   // force a server wipe when the version is lower than ours (or there is none)
  343.   _versionCheck: function WeaveSvc__versionCheck() {
  344.     let self = yield;
  345.  
  346.     let ret = yield DAV.GET("meta/version", self.cb);
  347.  
  348.     if (ret.status == 404) {
  349.       this._log.info("Could not get version file.  Wiping server data.");
  350.       yield this._serverWipe.async(this, self.cb);
  351.       yield this._uploadVersion.async(this, self.cb);
  352.  
  353.     } else if (!Utils.checkStatus(ret.status)) {
  354.       this._log.debug("Could not get version file from server");
  355.       self.done(false);
  356.       return;
  357.  
  358.     } else if (ret.responseText < STORAGE_FORMAT_VERSION) {
  359.       this._log.info("Server version too low.  Wiping server data.");
  360.       yield this._serverWipe.async(this, self.cb);
  361.       yield this._uploadVersion.async(this, self.cb);
  362.  
  363.     } else if (ret.responseText > STORAGE_FORMAT_VERSION) {
  364.       // XXX should we do something here?
  365.       throw "Server version higher than this client understands.  Aborting."
  366.     }
  367.     self.done(true);
  368.   },
  369.  
  370.   _checkUserDir: function WeaveSvc__checkUserDir() {
  371.     let self = yield;
  372.     let prefix = DAV.defaultPrefix;
  373.  
  374.     this._log.trace("Checking user directory exists");
  375.  
  376.     try {
  377.       DAV.defaultPrefix = '';
  378.       DAV.MKCOL("user/" + this.userPath, self.cb);
  379.       let ret = yield;
  380.       if (!ret)
  381.         throw "Could not create user directory";
  382.     }
  383.     catch (e) { throw e; }
  384.     finally { DAV.defaultPrefix = prefix; }
  385.   },
  386.  
  387.   // Retrieves the keypair for the given Identity object and inserts
  388.   // its information into the Identity object.  If no Identity object
  389.   // is supplied, the 'WeaveCryptoID' identity is used.
  390.   //
  391.   // This coroutine assumes the DAV singleton's prefix is set to the
  392.   // proper user-specific directory.
  393.   //
  394.   // If the password associated with the Identity cannot be used to
  395.   // decrypt the private key, an exception is raised.
  396.   _getKeypair : function WeaveSvc__getKeypair(id) {
  397.     let self = yield;
  398.  
  399.     if ("none" == Utils.prefs.getCharPref("encryption"))
  400.       return;
  401.  
  402.     if (typeof(id) == "undefined")
  403.       id = ID.get('WeaveCryptoID');
  404.  
  405.     this._log.trace("Retrieving keypair from server");
  406.  
  407.     if (this._keyPair['private'] && this._keyPair['public'])
  408.       this._log.debug("Using cached keypair");
  409.     else {
  410.       this._log.debug("Fetching keypair from server");
  411.  
  412.       let privkeyResp = yield DAV.GET("private/privkey", self.cb);
  413.       Utils.ensureStatus(privkeyResp.status, "Could not download private key");
  414.  
  415.       let pubkeyResp = yield DAV.GET("public/pubkey", self.cb);
  416.       Utils.ensureStatus(pubkeyResp.status, "Could not download public key");
  417.  
  418.       this._keyPair['private'] = this._json.decode(privkeyResp.responseText);
  419.       this._keyPair['public'] = this._json.decode(pubkeyResp.responseText);
  420.     }
  421.  
  422.     let privkeyData = this._keyPair['private']
  423.     let pubkeyData  = this._keyPair['public'];
  424.  
  425.     if (!privkeyData || !pubkeyData)
  426.       throw "Bad keypair JSON";
  427.     if (privkeyData.version != 1 || pubkeyData.version != 1)
  428.       throw "Unexpected keypair data version";
  429.     if (privkeyData.algorithm != "RSA" || pubkeyData.algorithm != "RSA")
  430.       throw "Only RSA keys currently supported";
  431.     if (!privkeyData.privkey)
  432.       throw "Private key does not contain private key data!";
  433.     if (!pubkeyData.pubkey)
  434.       throw "Public key does not contain public key data!";
  435.  
  436.  
  437.     id.keypairAlg     = privkeyData.algorithm;
  438.     id.privkey        = privkeyData.privkey;
  439.     id.privkeyWrapIV  = privkeyData.privkeyIV;
  440.     id.passphraseSalt = privkeyData.privkeySalt;
  441.  
  442.     id.pubkey = pubkeyData.pubkey;
  443.  
  444.     let isValid = yield Crypto.isPassphraseValid.async(Crypto, self.cb, id);
  445.     if (!isValid)
  446.       throw new Error("Passphrase is not valid.");
  447.   },
  448.  
  449.   _generateKeys: function WeaveSvc__generateKeys() {
  450.     let self = yield;
  451.  
  452.     this._log.debug("Generating new RSA key");
  453.  
  454.     // RSAkeygen will set the needed |id| properties.
  455.     let id = ID.get('WeaveCryptoID');
  456.     Crypto.RSAkeygen.async(Crypto, self.cb, id);
  457.     yield;
  458.  
  459.     DAV.MKCOL("private/", self.cb);
  460.     let ret = yield;
  461.     if (!ret)
  462.       throw "Could not create private key directory";
  463.  
  464.     DAV.MKCOL("public/", self.cb);
  465.     ret = yield;
  466.     if (!ret)
  467.       throw "Could not create public key directory";
  468.  
  469.     let privkeyData = { version     : 1,
  470.                         algorithm   : id.keypairAlg,
  471.                         privkey     : id.privkey,
  472.                         privkeyIV   : id.privkeyWrapIV,
  473.                         privkeySalt : id.passphraseSalt
  474.                       };
  475.     let data = this._json.encode(privkeyData);
  476.  
  477.     DAV.PUT("private/privkey", data, self.cb);
  478.     ret = yield;
  479.     Utils.ensureStatus(ret.status, "Could not upload private key");
  480.  
  481.  
  482.     let pubkeyData = { version   : 1,
  483.                        algorithm : id.keypairAlg,
  484.                        pubkey    : id.pubkey
  485.                      };
  486.     data = this._json.encode(pubkeyData);
  487.  
  488.     DAV.PUT("public/pubkey", data, self.cb);
  489.     ret = yield;
  490.     Utils.ensureStatus(ret.status, "Could not upload public key");
  491.   },
  492.  
  493.   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
  494.                                          Ci.nsISupportsWeakReference]),
  495.  
  496.   // nsIObserver
  497.  
  498.   observe: function WeaveSvc__observe(subject, topic, data) {
  499.     switch (topic) {
  500.       case "nsPref:changed":
  501.         switch (data) {
  502.           case "enabled": // this works because this.schedule is 0 when disabled
  503.           case "schedule":
  504.             this._setSchedule(this.schedule);
  505.             break;
  506.         }
  507.         break;
  508.       case "quit-application":
  509.         this._onQuitApplication();
  510.         break;
  511.     }
  512.   },
  513.  
  514.   _onQuitApplication: function WeaveSvc__onQuitApplication() {
  515.     if (!this.enabled || !this._loggedIn)
  516.       return;
  517.  
  518.     // Don't quit on exit if this is a forced restart due to application update
  519.     // or extension install.
  520.     var prefBranch = Cc["@mozilla.org/preferences-service;1"].
  521.                                         getService(Ci.nsIPrefBranch);
  522.     // non browser apps may throw
  523.     try {
  524.       if(prefBranch.getBoolPref("browser.sessionstore.resume_session_once"))
  525.         return;
  526.     } catch (ex) {}
  527.  
  528.     this.isQuitting = true;
  529.  
  530.     let ww = Cc["@mozilla.org/embedcomp/window-watcher;1"].
  531.              getService(Ci.nsIWindowWatcher);
  532.  
  533.     // This window has to be modal to prevent the application from quitting
  534.     // until the sync finishes and the window closes.
  535.     let window = ww.openWindow(null,
  536.                                "chrome://weave/content/status.xul",
  537.                                "Weave:Status",
  538.                                "chrome,centerscreen,modal,close=0",
  539.                                null);
  540.   },
  541.  
  542.   // These are global (for all engines)
  543.  
  544.   verifyPassphrase: function WeaveSvc_verifyPassphrase(onComplete, username,
  545.                                                        password, passphrase) {
  546.     this._localLock(this._notify("verify-passphrase", "", this._verifyPassphrase,
  547.                                  username, password, passphrase)).
  548.       async(this, onComplete);
  549.   },
  550.  
  551.   _verifyPassphrase: function WeaveSvc__verifyPassphrase(username, password,
  552.                                                          passphrase) {
  553.     let self = yield;
  554.  
  555.     this._log.debug("Verifying passphrase");
  556.  
  557.     this.username = username;
  558.     ID.get('WeaveID').setTempPassword(password);
  559.     
  560.     let id = new Identity('Passphrase Verification', username);
  561.     id.setTempPassword(passphrase);
  562.  
  563.     // FIXME: abstract common bits and share code with getKeypair()
  564.  
  565.     // XXX: We're not checking the version of the server here, in part because
  566.     // we have no idea what to do if the version is different than we expect
  567.     // it to be.
  568.     // XXX check it and ... throw?
  569.  
  570.     this.username = username;
  571.     ID.get('WeaveID').setTempPassword(password);
  572.  
  573.     let privkeyResp = yield DAV.GET("private/privkey", self.cb);
  574.     let pubkeyResp = yield DAV.GET("public/pubkey", self.cb);
  575.  
  576.     // FIXME: this will cause 404's to turn into a 'success'
  577.     // while technically incorrect, this function currently only gets
  578.     // called from the wizard, which will do a loginAndInit, which
  579.     // will create the keys if necessary later
  580.     if (privkeyResp.status == 404 || pubkeyResp.status == 404)
  581.       return;
  582.  
  583.     Utils.ensureStatus(privkeyResp.status, "Could not download private key");
  584.     Utils.ensureStatus(privkeyResp.status, "Could not download public key");
  585.  
  586.     let privkey = this._json.decode(privkeyResp.responseText);
  587.     let pubkey = this._json.decode(pubkeyResp.responseText);
  588.  
  589.     if (!privkey || !pubkey)
  590.       throw "Bad keypair JSON";
  591.     if (privkey.version != 1 || pubkey.version != 1)
  592.       throw "Unexpected keypair data version";
  593.     if (privkey.algorithm != "RSA" || pubkey.algorithm != "RSA")
  594.       throw "Only RSA keys currently supported";
  595.     if (!privkey.privkey)
  596.       throw "Private key does not contain private key data!";
  597.     if (!pubkey.pubkey)
  598.       throw "Public key does not contain public key data!";
  599.  
  600.     id.keypairAlg = privkey.algorithm;
  601.     id.privkey = privkey.privkey;
  602.     id.privkeyWrapIV = privkey.privkeyIV;
  603.     id.passphraseSalt = privkey.privkeySalt;
  604.     id.pubkey = pubkey.pubkey;
  605.  
  606.     if (!(yield Crypto.isPassphraseValid.async(Crypto, self.cb, id)))
  607.       throw new Error("Passphrase is not valid.");
  608.   },
  609.  
  610.   verifyLogin: function WeaveSvc_verifyLogin(onComplete, username, password) {
  611.     this._localLock(this._notify("verify-login", "", this._verifyLogin,
  612.                                  username, password)).async(this, onComplete);
  613.   },
  614.  
  615.   _verifyLogin: function WeaveSvc__verifyLogin(username, password) {
  616.     let self = yield;
  617.  
  618.     this._log.debug("Verifying login for user " + username);
  619.  
  620.     DAV.baseURL = Utils.prefs.getCharPref("serverURL");
  621.     DAV.defaultPrefix = "user/" + username;
  622.  
  623.     this._log.config("Using server URL: " + DAV.baseURL + DAV.defaultPrefix);
  624.  
  625.     let status = yield DAV.checkLogin.async(DAV, self.cb, username, password);
  626.     Utils.ensureStatus(status, "Login verification failed");
  627.   },
  628.  
  629.   loginAndInit: function WeaveSvc_loginAndInit(onComplete,
  630.                                                username, password, passphrase) {
  631.     this._localLock(this._notify("login", "", this._loginAndInit,
  632.                                  username, password, passphrase)).
  633.       async(this, onComplete);
  634.   },
  635.   _loginAndInit: function WeaveSvc__loginAndInit(username, password, passphrase) {
  636.     let self = yield;
  637.     try {
  638.       yield this._login.async(this, self.cb, username, password, passphrase);
  639.     } catch (e) {
  640.       // we might need to initialize before login will work (e.g. to create the
  641.       // user directory), so do this and try again...
  642.       yield this._initialize.async(this, self.cb);
  643.       yield this._login.async(this, self.cb, username, password, passphrase);
  644.     }
  645.     yield this._initialize.async(this, self.cb);
  646.   },
  647.  
  648.   login: function WeaveSvc_login(onComplete, username, password, passphrase) {
  649.     this._localLock(
  650.       this._notify("login", "", this._login,
  651.                    username, password, passphrase)).async(this, onComplete);
  652.   },
  653.   _login: function WeaveSvc__login(username, password, passphrase) {
  654.     let self = yield;
  655.  
  656.     this._log.debug("Logging in user " + this.username);
  657.  
  658.     if (typeof(username) != 'undefined')
  659.       this.username = username;
  660.     if (typeof(password) != 'undefined')
  661.       ID.get('WeaveID').setTempPassword(password);
  662.     if (typeof(passphrase) != 'undefined')
  663.       ID.get('WeaveCryptoID').setTempPassword(passphrase);
  664.  
  665.     if (!this.username)
  666.       throw "No username set, login failed";
  667.     if (!this.password)
  668.       throw "No password given or found in password manager";
  669.  
  670.     yield this._verifyLogin.async(this, self.cb, this.username, this.password);
  671.  
  672.     this._loggedIn = true;
  673.     self.done(true);
  674.   },
  675.  
  676.   initialize: function WeaveSvc_initialize() {
  677.     this._localLock(
  678.       this._notify("initialize", "", this._initialize)).async(this, onComplete);
  679.   },
  680.  
  681.   _initialize: function WeaveSvc__initialize() {
  682.     let self = yield;
  683.  
  684.     this._log.info("Making sure server is initialized...");
  685.  
  686.     // create user directory (for self-hosted webdav shares) if it doesn't exist
  687.     let status = yield DAV.checkLogin.async(DAV, self.cb,
  688.                                             this.username, this.password);
  689.     if (status == 404) {
  690.       yield this._checkUserDir.async(this, self.cb);
  691.       status = yield DAV.checkLogin.async(DAV, self.cb,
  692.                                           this.username, this.password);
  693.     }
  694.     Utils.ensureStatus(status, "Cannot initialize server");
  695.  
  696.     // wipe the server if it has any old cruft
  697.     yield this._versionCheck.async(this, self.cb);
  698.  
  699.     // get info on the clients that are syncing with this store
  700.     yield ClientData.refresh(self.cb);
  701.  
  702.     // cache keys, create public/private keypair if it doesn't exist
  703.     this._log.debug("Caching keys");
  704.     let privkeyResp = yield DAV.GET("private/privkey", self.cb);
  705.     let pubkeyResp = yield DAV.GET("public/pubkey", self.cb);
  706.  
  707.     if (privkeyResp.status == 404 || pubkeyResp.status == 404) {
  708.       yield this._generateKeys.async(this, self.cb);
  709.       privkeyResp = yield DAV.GET("private/privkey", self.cb);
  710.       pubkeyResp = yield DAV.GET("public/pubkey", self.cb);
  711.     }
  712.  
  713.     Utils.ensureStatus(privkeyResp.status, "Cannot initialize privkey");
  714.     Utils.ensureStatus(pubkeyResp.status, "Cannot initialize pubkey");
  715.  
  716.     this._keyPair['private'] = this._json.decode(privkeyResp.responseText);
  717.     this._keyPair['public'] = this._json.decode(pubkeyResp.responseText);
  718.  
  719.     yield this._getKeypair.async(this, self.cb); // makes sure passphrase works
  720.  
  721.     this._setSchedule(this.schedule);
  722.  
  723.     this._initialized = true;
  724.     self.done(true);
  725.   },
  726.  
  727.   logout: function WeaveSvc_logout() {
  728.     this._log.info("Logging out");
  729.     this._disableSchedule();
  730.     this._loggedIn = false;
  731.     this._initialized = false;
  732.     this._keyPair = {};
  733.     ID.get('WeaveID').setTempPassword(null); // clear cached password
  734.     ID.get('WeaveCryptoID').setTempPassword(null); // and passphrase
  735.     this._os.notifyObservers(null, "weave:service:logout:success", "");
  736.   },
  737.  
  738.   resetLock: function WeaveSvc_resetLock(onComplete) {
  739.     this._notify("reset-server-lock", "",
  740.                  this._resetLock).async(this, onComplete);
  741.   },
  742.   _resetLock: function WeaveSvc__resetLock() {
  743.     let self = yield;
  744.     DAV.forceUnlock.async(DAV, self.cb);
  745.     yield;
  746.   },
  747.  
  748.   serverWipe: function WeaveSvc_serverWipe(onComplete) {
  749.     let cb = function WeaveSvc_serverWipeCb() {
  750.       let self = yield;
  751.       this._serverWipe.async(this, self.cb);
  752.       yield;
  753.       this.logout();
  754.       self.done();
  755.     };
  756.     this._notify("server-wipe", "", this._lock(cb)).async(this, onComplete);
  757.   },
  758.   _serverWipe: function WeaveSvc__serverWipe() {
  759.     let self = yield;
  760.  
  761.     this._keyPair = {};
  762.     DAV.listFiles.async(DAV, self.cb);
  763.     let names = yield;
  764.  
  765.     for (let i = 0; i < names.length; i++) {
  766.       if (names[i].match(/\.htaccess$/))
  767.         continue;
  768.       DAV.DELETE(names[i], self.cb);
  769.       let resp = yield;
  770.       this._log.debug(resp.status);
  771.     }
  772.   },
  773.  
  774.   // These are per-engine
  775.  
  776.   sync: function WeaveSvc_sync(onComplete) {
  777.     this._notify("sync", "",
  778.                  this._catchAll(this._lock(this._sync))).async(this, onComplete);
  779.   },
  780.  
  781.   _sync: function WeaveSvc__sync() {
  782.     let self = yield;
  783.  
  784.     yield ClientData.refresh(self.cb);
  785.  
  786.     let engines = Engines.getAll();
  787.     for (let i = 0; i < engines.length; i++) {
  788.       if (this.cancelRequested)
  789.         continue;
  790.  
  791.       if (!engines[i].enabled)
  792.         continue;
  793.  
  794.       yield this._notify(engines[i].name + "-engine:sync", "",
  795.                          this._syncEngine, engines[i]).async(this, self.cb);
  796.     }
  797.  
  798.     if (this._syncError) {
  799.       this._syncError = false;
  800.       throw "Some engines did not sync correctly";
  801.     }
  802.   },
  803.  
  804.   // The values that engine scores must meet or exceed before we sync them
  805.   // as needed.  These are engine-specific, as different kinds of data change
  806.   // at different rates, so we store them in a hash indexed by engine name.
  807.   _syncThresholds: {},
  808.  
  809.   _syncAsNeeded: function WeaveSvc__syncAsNeeded() {
  810.     let self = yield;
  811.  
  812.     let engines = Engines.getAll();
  813.     for each (let engine in engines) {
  814.       if (!engine.enabled)
  815.         continue;
  816.  
  817.       if (!(engine.name in this._syncThresholds))
  818.         this._syncThresholds[engine.name] = INITIAL_THRESHOLD;
  819.  
  820.       let score = engine._tracker.score;
  821.       if (score >= this._syncThresholds[engine.name]) {
  822.         this._log.debug(engine.name + " score " + score +
  823.                         " reaches threshold " +
  824.                         this._syncThresholds[engine.name] + "; syncing");
  825.         this._notify(engine.name + "-engine:sync", "",
  826.                      this._syncEngine, engine).async(this, self.cb);
  827.         yield;
  828.  
  829.         // Reset the engine's threshold to the initial value.
  830.         // Note: we do this after syncing the engine so that we'll try again
  831.         // next time around if syncing fails for some reason.  The upside
  832.         // of this approach is that we'll sync again as soon as possible;
  833.         // but the downside is that if the error is caused by the server being
  834.         // overloaded, we'll contribute to the problem by trying to sync
  835.         // repeatedly at the maximum rate.
  836.         this._syncThresholds[engine.name] = INITIAL_THRESHOLD;
  837.       }
  838.       else {
  839.         this._log.debug(engine.name + " score " + score +
  840.                         " does not reach threshold " +
  841.                         this._syncThresholds[engine.name] + "; not syncing");
  842.  
  843.         // Decrement the threshold by the standard amount, and if this puts it
  844.         // at or below zero, then set it to 1, the lowest possible value, where
  845.         // it'll stay until there's something to sync (whereupon we'll sync it,
  846.         // reset the threshold to the initial value, and start over again).
  847.         this._syncThresholds[engine.name] -= THRESHOLD_DECREMENT_STEP;
  848.         if (this._syncThresholds[engine.name] <= 0)
  849.           this._syncThresholds[engine.name] = 1;
  850.       }
  851.     }
  852.  
  853.     if (this._syncError) {
  854.       this._syncError = false;
  855.       throw "Some engines did not sync correctly";
  856.     }
  857.   },
  858.  
  859.   _syncEngine: function WeaveSvc__syncEngine(engine) {
  860.     let self = yield;
  861.     try {
  862.       yield engine.sync(self.cb);
  863.       engine._tracker.resetScore();
  864.     } catch(e) {
  865.       // FIXME: FT module is not printing out exceptions - it should be
  866.       this._log.warn("Engine exception: " + e);
  867.       let ok = FaultTolerance.Service.onException(e);
  868.       if (!ok)
  869.         this._syncError = true;
  870.     }
  871.   },
  872.  
  873.   resetServer: function WeaveSvc_resetServer(onComplete) {
  874.     this._notify("reset-server", "",
  875.                  this._lock(this._resetServer)).async(this, onComplete);
  876.   },
  877.   _resetServer: function WeaveSvc__resetServer() {
  878.     let self = yield;
  879.  
  880.     let engines = Engines.getAll();
  881.     for (let i = 0; i < engines.length; i++) {
  882.       if (!engines[i].enabled)
  883.         continue;
  884.       engines[i].resetServer(self.cb);
  885.       yield;
  886.     }
  887.   },
  888.  
  889.   resetClient: function WeaveSvc_resetClient(onComplete) {
  890.     this._localLock(this._notify("reset-client", "",
  891.                                  this._resetClient)).async(this, onComplete);
  892.   },
  893.   _resetClient: function WeaveSvc__resetClient() {
  894.     let self = yield;
  895.     let engines = Engines.getAll();
  896.     for (let i = 0; i < engines.length; i++) {
  897.       if (!engines[i].enabled)
  898.         continue;
  899.       engines[i].resetClient(self.cb);
  900.       yield;
  901.     }
  902.   },
  903.  
  904.   shareData: function WeaveSvc_shareData(dataType,
  905.                      isShareEnabled,
  906.                                          onComplete,
  907.                                          guid,
  908.                                          username) {
  909.     /* Shares data of the specified datatype (which must correspond to
  910.        one of the registered engines) with the user specified by username.
  911.        The data node indicated by guid will be shared, along with all its
  912.        children, if it has any.  onComplete is a function that will be called
  913.        when sharing is done; it takes an argument that will be true or false
  914.        to indicate whether sharing succeeded or failed.
  915.        Implementation, as well as the interpretation of what 'guid' means,
  916.        is left up to the engine for the specific dataType.
  917.  
  918.        isShareEnabled: true to start sharing, false to stop sharing.*/
  919.  
  920.     let messageName = "share-" + dataType;
  921.     /* so for instance, if dataType is "bookmarks" then a message
  922.      "share-bookmarks" will be sent out to any observers who are listening
  923.      for it.  As far as I know, there aren't currently any listeners for
  924.      "share-bookmarks" but we'll send it out just in case. */
  925.  
  926.     let self = this;
  927.     let saved_dataType = dataType;
  928.     let saved_onComplete = onComplete;
  929.     let saved_guid = guid;
  930.     let saved_username = username;
  931.     let saved_isShareEnabled = isShareEnabled;
  932.     let successMsg = "weave:service:global:success";
  933.     let errorMsg = "weave:service:global:error";
  934.     let os = Cc["@mozilla.org/observer-service;1"].
  935.                       getService(Ci.nsIObserverService);
  936.  
  937.     let observer = {
  938.       observe: function(subject, topic, data) {
  939.     if (!Weave.DAV.locked) {
  940.           self._notify(messageName, "", self._lock(self._shareData,
  941.                     saved_dataType,
  942.                     saved_isShareEnabled,
  943.                                         saved_guid,
  944.                                     saved_username)).async(self,
  945.                                saved_onComplete);
  946.       os.removeObserver(observer, successMsg);
  947.       os.removeObserver(observer, errorMsg);
  948.     }
  949.       },
  950.       QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver])
  951.     };
  952.  
  953.     if (Weave.DAV.locked) {
  954.       /* then we have to wait until it's not locked. */
  955.       dump( "DAV is locked, gonna set up observer to do it later.\n");
  956.       os.addObserver( observer, successMsg, true );
  957.       os.addObserver( observer, errorMsg, true );
  958.     } else {
  959.       // Just do it right now!
  960.       dump( "DAV not locked, doing it now.\n");
  961.       observer.observe();
  962.     }
  963.   },
  964.  
  965.   _shareData: function WeaveSvc__shareData(dataType,
  966.                        isShareEnabled,
  967.                                            guid,
  968.                                            username) {
  969.     let self = yield;
  970.     let ret;
  971.     if (Engines.get(dataType).enabled) {
  972.       if (isShareEnabled) {
  973.         Engines.get(dataType).share(self.cb, guid, username);
  974.       } else {
  975.         Engines.get(dataType).stopSharing(self.cb, guid, username);
  976.       }
  977.       ret = yield;
  978.     } else {
  979.       this._log.warn( "Can't share disabled data type: " + dataType );
  980.       ret = false;
  981.     }
  982.     self.done(ret);
  983.   }
  984. };
  985.